package main

import(
	"fmt"
	"reflect"
	"unsafe"
)

func main()  {
	// ss:=[]string{"飞雪无情","张三"}
	// fmt.Println("切片ss长度为",len(ss),",容量为",cap(ss))
	// // append 自动扩容的原理是新创建一个底层数组，把原来切片内的元素拷贝到新数组中，然后再返回一个指向新数组的切片
	// ss=append(ss,"zhangsan","lisi")
	// fmt.Println("切片ss长度为",len(ss),",容量为",cap(ss))
	// fmt.Println(ss)

	/*  SliceHeader 是切片在运行时的表现形式，它有三个字段 Data、Len 和 Cap。
		Data 用来指向存储切片元素的数组。

		Len 代表切片的长度。

		Cap 代表切片的容量。
	*/
	// a1:=[2]string{"飞雪无情","张三"}
	// s1:=a1[0:1]
	// s2:=a1[:]
	// fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s1)).Data)
	// fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s2)).Data)
	/*
		注意：多个切片共用一个底层数组虽然可以减少内存占用，
		但是如果有一个切片修改内部的元素，其他切片也会受影响。所以在切片作为参数在函数间传递的时候要小心，尽可能不要修改原切片内的元素。
		我们还是尽可能地用 SliceHeader，因为这是 Go 语言提供的标准，可以保持统一，便于理解。
		高效的原因：
		数组和切片的取值和赋值操作要更高效
		因为他们是连续内存操作
		通过索引可快速找到元素存储的地址
	*/

//    a1:=[2]string{"飞雪无情","张三"}
//    fmt.Printf("函数main数组指针：%p\n",&a1)
//    arrayF(a1)
//    s1:=a1[0:1]
//    fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s1)).Data)
//    sliceF(s1)

	// string []byte互转
	// s:="飞雪无情"
	// b:=[]byte(s)
	// s3:=string(b)
	// fmt.Println(s,string(b),s3)

	// 强制转换重新分配内存
	// s:="zhangsan"
	// fmt.Printf("s的内存地址：%d\n",(*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
	// b:=[]byte(s)
	// fmt.Printf("b的内存地址：%d\n",(*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
	// s3:=string(b)
	// fmt.Printf("s3的内存地址：%d\n", (*reflect.StringHeader)(unsafe.Pointer(&s3)).Data)

	// s:="飞雪无情"
	// b:=[]byte(s)
	// //s3:=string(b)
	// s4:=*(*string)(unsafe.Pointer(&b)) // 零拷贝 使用同一块内存
	// fmt.Println(s4)

	s:="飞雪无情"
	//b:=[]byte(s)	
	sh:=(*reflect.SliceHeader)(unsafe.Pointer(&s))
	sh.Cap = sh.Len
	b1:=*(*[]byte)(unsafe.Pointer(sh))	
	fmt.Println(b1)
	/*
		通过 unsafe.Pointer 把 string 转为 []byte 后，不能对 []byte 修改，比如不可以进行 b1[0]=12 这种操作，会报异常，导致程序崩溃。
		这是因为在 Go 语言中 string 内存是只读的。
	*/
}

func arrayF(a [2]string)  {
	fmt.Printf("函数arrayF数组指针：%p\n",&a)
}

func sliceF(s []string)  {
	fmt.Printf("函数sliceF Data：%d\n",(*reflect.SliceHeader)(unsafe.Pointer(&s)).Data)
}